﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Krile.Kernel.Control.Surface;
using System.Drawing;

namespace Krile.Forms.MainForm.Controls.Surface
{
    internal class SurfaceMouseManager
    {
        SurfaceCore parent;
        SurfaceRenderer renderer;
        IMouseControllable lastPointed;
        string lastTooltipText;
        ToolTip toolTip;

        internal SurfaceMouseManager(SurfaceCore parent, SurfaceRenderer renderer)
        {
            this.parent = parent;
            this.renderer = renderer;
            //initialize
            lastPointed = null;
            lastTooltipText = null;
            toolTip = new ToolTip();
            toolTip.ShowAlways = true;
            toolTip.Active = false;

            //events set
            parent.MouseLeave += new EventHandler(parent_MouseLeave);
            parent.MouseMove += new MouseEventHandler(parent_MouseMove);
            parent.MouseClick += new MouseEventHandler(parent_MouseClick);
            parent.MouseDoubleClick += new MouseEventHandler(parent_MouseDoubleClick);
            parent.MouseUp += new MouseEventHandler(parent_MouseUp);
            parent.MouseDown += new MouseEventHandler(parent_MouseDown);
            parent.MouseWheel += new MouseEventHandler(parent_MouseWheel);
            parent.OnSelectedItemChanged += new Action(parent_OnSelectedItemChanged);
        }

        const int DEFAULT_DELTA = 120;
        void parent_MouseWheel(object sender, MouseEventArgs e)
        {
            if (e.Delta == 0) return;
            //↑ +
            //↓ -
            int delta = e.Delta / DEFAULT_DELTA;
            long nval = parent.VerticalPosition - delta * Math.Abs(delta) *Core.Config.Behavior.WheelScrollHeight;
            if (nval < 0)
                nval = 0;
            parent.VerticalPosition = nval;
        }

        void parent_MouseDown(object sender, MouseEventArgs e)
        {
            ChainMouseEventArgs xe = ChainMouseEventArgs.Factory(parent.GetParentForm(), e);
            foreach (var tmci in GetItemByPoint(e.Location))
            {
                tmci.OnMouseDown(sender, xe);
                if (!xe.IsThrowedSub)
                    break;
                xe.SetTerminate();
            }
        }

        void parent_MouseUp(object sender, MouseEventArgs e)
        {
            ChainMouseEventArgs xe = ChainMouseEventArgs.Factory(parent.GetParentForm(), e);
            foreach (var tmci in GetItemByPoint(e.Location))
            {
                tmci.OnMouseUp(sender, xe);
                if (!xe.IsThrowedSub)
                    break;
                xe.SetTerminate();
            }
        }

        void parent_MouseClick(object sender, MouseEventArgs e)
        {
            ChainMouseEventArgs xe = ChainMouseEventArgs.Factory(parent.GetParentForm(), e);
            foreach (var tmci in GetItemByPoint(e.Location))
            {
                tmci.OnMouseClick(sender, xe);
                if (!xe.IsThrowedSub)
                    break;
                xe.SetTerminate();
            }
            parent_MouseMove(sender, e);
        }

        void parent_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            ChainMouseEventArgs xe = ChainMouseEventArgs.Factory(parent.GetParentForm(), e);
            foreach (var tmci in GetItemByPoint(e.Location))
            {
                tmci.OnMouseDoubleClick(sender, xe);
                if (!xe.IsThrowedSub)
                    break;
                xe.SetTerminate();
            }
        }

        void parent_MouseMove(object sender, MouseEventArgs e)
        {
            ChainMouseEventArgs xe = ChainMouseEventArgs.Factory(parent.GetParentForm(), e);
            if (e.Button == MouseButtons.Left && lastPointed != null)
            {
                lastPointed.OnMouseMove(sender, xe);
            }
            else
            {
                foreach (var tmci in GetItemByPoint(e.Location))
                {
                    //チップヘルプ処理
                    if (!tmci.Equals(lastPointed) || tmci.HelptipText != lastTooltipText)
                    {
                        //以前指していたオブジェクトと違うか、またはヘルプテキストが変更されていたら再セット
                        releaseLastPointed();
                        lastPointed = tmci;
                        if (tmci == null)
                            return;
                        if (!String.IsNullOrEmpty(tmci.HelptipText))
                        {
                            toolTip.SetToolTip(parent, tmci.HelptipText);
                            toolTip.Active = true;
                        }
                        lastTooltipText = tmci.HelptipText;

                        tmci.OnMouseEnter(sender, xe);
                        return;
                    }
                    //イベント呼び出し
                    tmci.OnMouseMove(sender, xe);
                    return;
                }
                releaseLastPointed();
            }
        }

        void parent_MouseLeave(object sender, EventArgs e)
        {
            releaseLastPointed();
            var parn = parent.GetParentForm();
            if(parn != null)
                parn.SetCursor(Cursors.Default);
        }

        object itemsLocker = new object();
        IMouseControllable[] items = null;
        void parent_OnSelectedItemChanged()
        {
            lock (itemsLocker)
            {
                if (parent.SelectedStatus == null)
                    items = null;
                else
                    items = parent.SelectedStatus.BaseStatus.Text.MessageObjects;
                if (items != null)
                {
                    var pf = parent.GetParentForm();
                    foreach (var i in items)
                    {
                        i.ParentForm = pf;
                    }
                }
            }
        }

        /// <summary>
        /// 座標に衝突しているアイテムを検索し返します。
        /// </summary>
        /// <param name="location">対象座標</param>
        /// <returns>衝突判定されたアイテム</returns>
        IEnumerable<IMouseControllable> GetItemByPoint(Point location)
        {
            lock (itemsLocker)
                if (items != null)
                    foreach (var i in items)
                        if (i.HitTest(location))
                            yield return i;
            foreach (var ri in renderer.GetMouseControllables())
                if (ri.HitTest(location))
                    yield return ri;
        }

        /// <summary>
        /// 以前アクティブだったステータスのリソースを解放します。
        /// </summary>
        void releaseLastPointed()
        {
            if (lastPointed != null)
            {
                lastPointed.OnMouseLeave(parent, new ChainMouseEventArgs());
                lastPointed = null;
                toolTip.Active = false;
            }
        }

        public void Dispose()
        {
            toolTip.Dispose();
        }
    }
}
